package cn.net.dpi.core.trace;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import org.slf4j.Logger;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 链路监控辅助类
 * 数据关系解释：
 * name 可对应操作名称，比如：实例创建
 * traceId  链路表示，比如：实例 code
 * messageChain 链路信息，可以有序的记录操作过程中的信息方便追踪
 *
 * @author dangzhicairang
 */
@Getter
@Validated
public class MetaTrace implements TraceOuter {

    @NotBlank
    private String name;

    private String traceId;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date startTime;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date endTime;

    private List<String> messageChain = new ArrayList<>();

    // 拓展属性
    private Map<String, Object> customProperties = new HashMap<>();

    @JsonIgnore
    private boolean active = false;

    public MetaTrace(String name) {
        this(name, "");
    }

    public MetaTrace(String name, String traceId) {
        this.name = name;
        this.traceId = traceId;
    }

    public MetaTrace start() {
        return this.start(new Date());
    }

    public MetaTrace start(Date startTime) {
        this.startTime = startTime;
        this.active = true;
        return this;
    }

    public MetaTrace addMessage(@NotBlank String message) {
        if (!this.active) {
            throw new UnsupportedOperationException("当前 Trace 对象未开始或已结束");
        }
        this.messageChain.add(message);
        return this;
    }

    public MetaTrace end() {
        return this.end(new Date());
    }

    public MetaTrace end(Date endTime) {
        this.endTime = endTime;
        this.active = false;
        return this;
    }

    public MetaTrace prop(@NotNull String key, @NotNull Object value) {
        this.customProperties.put(key, value);
        return this;
    }

    public Object get(@NotNull String key) {
        return this.customProperties.get(key);
    }

    public boolean has(@NotNull String key) {
        return this.get(key) != null;
    }

    @Override
    public String prettyOut(Logger logger) {

        String separator = "-----------------------\n";
        StringBuilder sb = new StringBuilder();
        sb.append("\n");
        sb.append(separator);
        sb.append(this.traceId + "\n");
        sb.append(this.name + "\n");
        sb.append(formatDate(startTime) + "\n");

        for (int i = 0; i < this.messageChain.size(); i ++) {
            sb.append((i + 1) + "." + this.messageChain.get(i) + "\n");
        }

        try {
            String propJson = new ObjectMapper()
                    .writerWithDefaultPrettyPrinter()
                    .writeValueAsString(customProperties);
            sb.append("[~]自定义属性：\n");
            sb.append(propJson);
            sb.append("\n");
        } catch (JsonProcessingException e) {
        }

        sb.append(formatDate(endTime) + "\n");
        sb.append(separator);

        if (logger != null) {
            logger.info(sb.toString());
        }
        return sb.toString();
    }

    @JsonIgnore
    private SimpleDateFormat defaultSDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private String formatDate(Date date) {

        if (date == null) {
            return "";
        }

        return defaultSDF.format(date);
    }
}
